#coding: utf-8

import maya.cmds as cmds
import logging


def setup_logger():
    if not cmds.optionVar(q='Pencil.SmoothDebug'):
        class _Dummy(object):
            def info(self, *args):
                pass
        return _Dummy()

    logger = logging.getLogger(__name__)
    log_level = logging.INFO
    logger.setLevel(log_level)
    return logger

plogger = setup_logger()


#
# レンダリング範囲のキーフレームから可視状態を調べる
#
class VisibleJudgment(object):
    def __init__(self):
        self.startFrame = cmds.getAttr('defaultRenderGlobals.startFrame')
        self.endFrame = cmds.getAttr('defaultRenderGlobals.endFrame')
        self.isAnimation = cmds.getAttr('defaultRenderGlobals.animation')
        self.isBatch = cmds.about(q=True, batch=True)
        self.renderingSections = [(self.startFrame, self.endFrame)]


    def _findEqualValue(self, values, val, index):
        for i in range(index, len(values)):
            if int(values[i]) == int(val):
                return i
        return None
    def _findNotEqualValue(self, values, val, index):
        for i in range(index, len(values)):
            if int(values[i]) != int(val):
                return i
        return None

    # 特定の値を持つ区間を表すタプル配列を作る
    def _makeSectionArray(self, times, values, val):
        result = []
        index_l = -1
        index_r = -1

        while True:
            index_l = self._findEqualValue(values, val,  index_r+1)
            if index_l == None:
                return result

            index_r = self._findNotEqualValue(values, val, index_l+1)

            if index_r == None:
                begin = self.startFrame if index_l == 0 else times[index_l]
                result.append((begin, self.endFrame))
                return result
            else:
                begin = self.startFrame if index_l == 0 else times[index_l]
                result.append((begin, times[index_r]-1))


    # ノードアトリビュートのキーフレームから区間を表すタプル配列を作る
    def getSectionArray(self, node_attr, val):
        cnt = cmds.keyframe(node_attr, q=True, keyframeCount=True)
        if cnt > 0:
            times = cmds.keyframe(node_attr, q=True, tc=True, index=(0,cnt))
            values= cmds.keyframe(node_attr, q=True, vc=True, index=(0,cnt))
            return self._makeSectionArray(times, values, val)
        else:
            if int(cmds.getAttr(node_attr)) == int(val):
                return self.renderingSections
            else:
                return []


    # section1とsection2の交差部分の配列を返す
    # 配列の要素数は0～1個
    def _intersectSection1_1(self, section1, section2):
        if section2[1] < section1[0] or section2[0] > section1[1]:
            #交差無し
            return []

        if section2[0] <= section1[0]:
            if section2[1] < section1[1]:
                #左端から一部交差
                return [(section1[0], section2[1])]
            else:
                #左端から全体を含む
                return [section1[:]]
        elif section2[1] >= section1[1]:
                #途中から全体を含む
                return [(section2[0], section1[1])]
        else:
                #中央部が交差
                return [section2[:]]

    # sectionsとsectionが交差する部分の配列を返す
    def _intersectSectionN_1(self, sections, section):
        result = []
        for i in range(len(sections)):
            array = self._intersectSection1_1(sections[i], section)
            result += array
        return result

    # sections1とsections2が交差する部分の配列を返す
    def inersectSection(self, sections1, sections2):
        result = []
        for sec in sections2:
            result += self._intersectSectionN_1(sections1, sec)
        return result


    # section1 - section2となる差分の配列を返す
    # 配列の要素数は0～2個
    def _removeSection1_1(self, section1, section2):
        if section2[1] < section1[0] or section2[0] > section1[1]:
            #交差無し
            return [section1[:]]

        if section2[0] <= section1[0]:
            if section2[1] < section1[1]:
                #左端から一部交差
                return [(section2[1] + 1, section1[1])]
            else:
                #左端から全体を含む
                return []
        elif section2[1] >= section1[1]:
                #途中から全体を含む
                return [(section1[0], section2[0]-1)]
        else:
                #中央部が交差
                return [(section1[0], section2[0]-1), (section2[1]+1, section1[1])]

    def _removeSectionN_1(self, sections, section):
        result = []
        for i in range(len(sections)):
            array = self._removeSection1_1(sections[i], section)
            result += array
        return result

    # sections1 - sections2となる差分の配列を返す
    def removeSection(self, sections1, sections2):
        result = sections1
        for sec in sections2:
            result = self._removeSectionN_1(result, sec)
        return result

    # カレントフレームのアトリビュートから可視判定
    def _isVisibleCurrentFrame(self, node):
        if not node or not cmds.getAttr(node + '.visibility') or cmds.getAttr(node + '.template'):
            return False
        if cmds.getAttr(node + '.overrideEnabled'):
            if not cmds.getAttr(node + '.overrideVisibility') or cmds.getAttr(node + '.overrideDisplayType') == 1:    # 1 == Template
                return False
        return True

    # レンダリング範囲のキーフレームから可視判定
    # ※各チャンネルにノードが接続されているときは、キーフレームとして取得できないので、不完全な判定となってしまう
    def _isVisibleKeyFrames(self, node):
        visible_sections = self.getSectionArray(node + '.visibility', 1)
        template_sections = self.getSectionArray(node + '.template', 1)
        result_sections = self.inersectSection(visible_sections, self.renderingSections)    #レンダリング範囲に限定
        result_sections = self.removeSection(result_sections, template_sections)

        override_sections = self.getSectionArray(node + '.overrideEnabled', 1)
        if (len(override_sections) > 0):
            override_invisible_sections = self.getSectionArray(node + '.overrideVisibility', 0)
            override_disptype_sections = self.getSectionArray(node + '.overrideDisplayType', 1)
            override_invisible_sections = self.inersectSection(override_sections, override_invisible_sections)
            override_disptype_sections = self.inersectSection(override_sections, override_disptype_sections)
            #オーバーライドされている不可視部分を削る
            result_sections = self.removeSection(result_sections, override_invisible_sections)
            result_sections = self.removeSection(result_sections, override_disptype_sections)

        #plogger.info('visible range : {} : {}'.format(node, result_sections))
        return len(result_sections) > 0

    # バッチ処理用の簡易判定
    def _isVisibleBatchAnimation(self, node):
        # 簡易的に、ノードが接続されていれば可視になる可能性ありと判断する（アニメーション全体をチェックするのは負荷が高いので）
        if cmds.connectionInfo(node + '.visibility', isDestination=True) or \
            cmds.connectionInfo(node + '.template', isDestination=True) or \
            cmds.connectionInfo(node + '.drawOverride', isDestination=True) or \
            cmds.connectionInfo(node + '.overrideEnabled', isDestination=True) or \
            cmds.connectionInfo(node + '.overrideVisibility', isDestination=True) or \
            cmds.connectionInfo(node + '.overrideDisplayType', isDestination=True):
                return True
        # ノードが接続されていないときは現在のチャンネル値から可視判定する
        return self._isVisibleCurrentFrame(node)


    # 可視判定
    def isVisible(self, node):
        if self.isBatch and self.isAnimation:
            return self._isVisibleBatchAnimation(node)
        else:
            return self._isVisibleCurrentFrame(node)

